﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;

namespace ProjetS5
{
    public enum SaveType
    {
        local,
    }
    static class Saver
    {
        private static Database database;
        private static bool isLoaded;

        public static void Load(Database toLoad)
        {
            database = toLoad;
            isLoaded = true;
        }
        public static void Unload()
        {
            database = null;
            isLoaded = false;
        }

        public static String encode(List<DatabaseTable> toEncode)
        {
            int maxId = database.Datas.Where(e => e.GetType() == toEncode.First().GetType()).Max(e => e.Identifier);
            int nbOfCharacter = (int)Math.Ceiling(maxId / 8.0);
            byte[] encodedByte = new byte[nbOfCharacter];
            for (int i = 0; i < nbOfCharacter; i++)
            {
                encodedByte[i] = 0;
            }
            foreach(DatabaseTable entry in toEncode)
            {
                int characterNb = entry.Identifier / 8;
                encodedByte[characterNb] += (byte)(Math.Pow(2, (entry.Identifier - 1) % 8));
            }

            return encodedByte.ToString();
        }
        public static List<int> decode(String toDecode)
        {
            List<int> decoded = new List<int>();

            for (int i = 0 ; i < toDecode.Length ; i++)
            {
                byte temp = (byte)toDecode[i];
                for (int j = 7 ; j >= 0 ; j--)
                {
                    if ((temp - (byte)Math.Pow(2, j)) >= 0)
                    {
                        byte toMinus = (byte)Math.Pow(2, j);
                        temp -= toMinus;
                        decoded.Add((int)(i*8 + j+1));
                    }
                }
            }

            return decoded;
        }

        public static List<Player> LoadPlayerIdentificationInfo()
        {
            List<Player> players = new List<Player>();

            return players;
        }

        public static List<String> GetFilesName()
        {
            List<String> names = new List<String>();
            String path = "/Save/";
            if (Directory.Exists(path))
            {
                names = (List<String>)Directory.EnumerateFiles(path);
                for (int i = 0 ; i < names.Count ; i++)
                {
                    names[i] = names[i].Substring(0, names[i].Length - 4);
                }
            }

            return names;
        }
        public static Me LoadPlayer(String playerName)
        {
            String path = "/Save/" + playerName + ".sav";
            FileStream save = File.OpenRead(path);
            byte[] fileDatas = null;
            save.Read(fileDatas, 0, (int)save.Length);
            String fullDatas = fileDatas.ToString();
            String hash = fullDatas.Substring(fullDatas.LastIndexOf('\n') + 1);
            String datas = fullDatas.Substring(0, fullDatas.LastIndexOf('\n') + 1);
            Me player = null;

            if (datas.GetHashCode().ToString() == hash)
            {
                //ligne 1
                int level = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int experience = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                Difficulty maxDifficultyCompleted = (Difficulty)Enum.Parse(typeof(Difficulty), datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1);

                //ligne 2
                int life = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int mana = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1);

                //ligne 3
                int raceId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int classId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int nbBars = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1); 

                //ligne 4
                List<List<int>> selectedTalentsId = new List<List<int>>();
                foreach (List<DB_TalentTree> trees in database.Datas.OfType<DB_CharacterClass>().Select(e => e.TalentTrees))
                {
                    selectedTalentsId.Add(decode(datas.Substring(0, datas.IndexOf('\n'))));
                    datas = datas.Substring(datas.IndexOf('\n') + 1);
                }

                //ligne 5
                Interface gameInterface = new Interface(nbBars);
                for (int i = 0; i < nbBars; i++)
                {
                    gameInterface.bars[i] = new InterfaceBar();
                    while (datas.IndexOf('\n') > datas.IndexOf('\t'))
                    {
                        int abilityId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                        datas = datas.Substring(datas.IndexOf('\t') + 1);
                        gameInterface.bars[i].abilities.Add(abilityId >= 0 ? new Ability(abilityId, database) : null);
                    }
                    int lastAbilityId = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                    datas = datas.Substring(datas.IndexOf('\n') + 1);
                    gameInterface.bars[i].abilities.Add(lastAbilityId >= 0 ? new Ability(lastAbilityId, database) : null);
                }

                //ligne 6
                Dictionary<int, int> equippedItemsId = new Dictionary<int,int>();
                while (datas.IndexOf('\n', datas.IndexOf('\t')) > datas.IndexOf('\t', datas.IndexOf('\t')))
                {
                    int equipmentSlotId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    int itemId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    equippedItemsId.Add(equipmentSlotId, itemId);
                }
                int lastEquipmentSlotId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int lastItemId = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1);
                equippedItemsId.Add(lastEquipmentSlotId, lastItemId);

                //ligne 7
                Dictionary<Vector2, KeyValuePair<int, int>> inventoryItems = new Dictionary<Vector2, KeyValuePair<int, int>>();
                while (datas.IndexOf('\n', datas.IndexOf('\t', datas.IndexOf('\t', datas.IndexOf('\t')))) > datas.IndexOf('\t', datas.IndexOf('\t', datas.IndexOf('\t', datas.IndexOf('\t')))))
                {
                    int itemId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    int posX = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    int posY = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    int quantity = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    inventoryItems.Add(new Vector2(posX, posY), new KeyValuePair<int,int>(itemId, quantity));
                }
                lastItemId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int lastPosX = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int lastPosY = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                int lastQuantity = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1);
                inventoryItems.Add(new Vector2(lastPosX, lastPosY), new KeyValuePair<int, int>(lastItemId, lastQuantity));

                //ligne 8
                List<int> doneQuestsId = decode(datas.Substring(0, datas.IndexOf('\t')));
                datas = datas.Substring(datas.IndexOf('\t') + 1);
                List<int> doneHFsId = decode(datas.Substring(0, datas.IndexOf('\n')));
                datas = datas.Substring(datas.IndexOf('\n') + 1);

                //ligne 9
                Dictionary<int, List<int>> inLogQuestsId = new Dictionary<int, List<int>>();
                while (datas.IndexOf('\t', datas.IndexOf('\n')) < datas.IndexOf('\n', datas.IndexOf('\n')))
                {
                    int questId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    inLogQuestsId.Add(questId, new List<int>());
                    while (datas.IndexOf('\n') > datas.IndexOf('\t'))
                    {
                        int avancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                        datas = datas.Substring(datas.IndexOf('\t') + 1);
                        inLogQuestsId.Last().Value.Add(avancementId);
                    }
                    int lastAvancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                    datas = datas.Substring(datas.IndexOf('\n') + 1);
                    inLogQuestsId.Last().Value.Add(lastAvancementId);
                }

                //ligne 10
                datas = datas.Substring(datas.IndexOf('\n') + 1);

                //ligne 11
                Dictionary<int, List<int>> inProgressHFsId = new Dictionary<int, List<int>>();
                try
                {
                    while (datas.IndexOf('\n') >= 0)
                    {
                        int achievementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                        datas = datas.Substring(datas.IndexOf('\t') + 1);
                        inProgressHFsId.Add(achievementId, new List<int>());
                        while (datas.IndexOf('\n') > datas.IndexOf('\t'))
                        {
                            int avancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                            datas = datas.Substring(datas.IndexOf('\t') + 1);
                            inProgressHFsId.Last().Value.Add(avancementId);
                        }
                        int lastAvancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                        datas = datas.Substring(datas.IndexOf('\n') + 1);
                        inProgressHFsId.Last().Value.Add(lastAvancementId);
                    }
                    
                }
                catch
                {
                    int lastAchievementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                    datas = datas.Substring(datas.IndexOf('\t') + 1);
                    inProgressHFsId.Add(lastAchievementId, new List<int>());
                    try
                    {
                        while (datas.IndexOf('\n') > datas.IndexOf('\t'))
                        {
                            int avancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\t')));
                            datas = datas.Substring(datas.IndexOf('\t') + 1);
                            inProgressHFsId.Last().Value.Add(avancementId);
                        }
                    }
                    catch
                    {
                        int veryLastAvancementId = Int32.Parse(datas.Substring(0, datas.IndexOf('\n')));
                        datas = datas.Substring(datas.IndexOf('\n') + 1);
                        inProgressHFsId.Last().Value.Add(veryLastAvancementId);
                    }
                }

                //construction
                player = new Me(database, playerName, level, experience, maxDifficultyCompleted, life, mana, raceId, classId, gameInterface, selectedTalentsId, equippedItemsId, inventoryItems, doneQuestsId, doneHFsId, inLogQuestsId, inProgressHFsId);
            }

            return player;
        }
        public static void SavePlayer(Player player)
        {
            String datas = "";

            // ligne 1
            datas += player.level + "\t" + player.experience + "\t" + (int)player.maxDifficultyCompleted + "\n";

            // ligne 2
            datas += player.pointBars[datas.OfType<DB_PointBar>().First()] + "\t" + player.pointBars[datas.OfType<DB_PointBar>().Last()] + "\n";

            // ligne 3
            datas += player.characterRace.Identifier + "\t" + player.characterClass.Identifier + "\n";

            //ligne 4
            DB_CharacterClass playersClass = database.Datas.OfType<DB_CharacterClass>().Single(e => e == player.characterClass);
            List<DB_TalentTree> trees = playersClass.TalentTrees;
            foreach (DB_TalentTree tree in trees)
            {
                List<DB_Talent> inTreeTalents = player.selectedTalents.Where(e => e.tree == tree).Select(e => e.talent).ToList<DB_Talent>();
                datas += encode(new List<DatabaseTable>(inTreeTalents)) + "\n";
            }

            // ligne 5
            foreach (InterfaceBar bar in ((Me)player).gameInterface.bars)
            {
                foreach (Ability ability in bar.abilities)
                {
                    datas += (ability == null ? -1 : ability.ability.Identifier) + "\t";
                }
                datas += "\n";
            }

            //ligne 6
            foreach (KeyValuePair<DB_EquipmentSlot, Item> kvpSlotItem in player.equipment.equippedItems)
            {
                datas += kvpSlotItem.Key.Identifier + "\t" + kvpSlotItem.Value.item.Identifier + "\t";
            }
            datas += "\n";

            //ligne 7
            foreach (KeyValuePair<Vector2, KeyValuePair<Item, int>> kvpInventoryItem in player.inventory.items)
            {
                datas += kvpInventoryItem.Value.Key + "\t" + kvpInventoryItem.Key.X + "\t" + kvpInventoryItem.Key.Y + "\t" + kvpInventoryItem.Value.Value + "\t";
            }
            datas += "\n";

            //ligne 8
            datas += encode(new List<DatabaseTable>(player.doneQuests));
            datas += "\t";
            datas += encode(new List<DatabaseTable>(player.doneAchievements));
            datas += "\n";

            //ligne 9
            foreach (KeyValuePair<DB_Quest, Dictionary<DB_Objective, int>> kvpInLogQuests in player.inLogQuests)
            {
                datas += kvpInLogQuests.Key.Identifier + "\t";
                foreach (KeyValuePair<DB_Objective, int> kvpObjectives in kvpInLogQuests.Value)
                {
                    datas += kvpObjectives.Value + "\t";
                }
                datas += "\n";
            }
                
            //ligne 10
            datas += "\n";

            //ligne 11
            foreach (KeyValuePair<DB_Achievement, Dictionary<DB_Objective, int>> kvpInProgressAchievements in player.inProgressAchievements)
            {
                datas += kvpInProgressAchievements.Key.Identifier + "\t";
                foreach (KeyValuePair<DB_Objective, int> kvpObjectives in kvpInProgressAchievements.Value)
                {
                    datas += kvpObjectives.Value + "\t";
                }
                datas += "\n";
            }
            
            // ligne 12
            int hash = datas.GetHashCode();
            datas += hash;

            String path = "/Save/" + player.name + ".sav";
            File.WriteAllText(path, datas);
        }
    }
}
